/**
 *
 */
package com.kontza.LocationLogger;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.location.Coordinates;
import javax.microedition.location.LocationException;
import javax.microedition.location.LocationListener;
import javax.microedition.location.LocationProvider;
import javax.microedition.location.QualifiedCoordinates;
import javax.microedition.location.Criteria;
import javax.microedition.location.Location;
import javax.microedition.m2g.SVGImage;
import org.netbeans.microedition.svg.SVGForm;
import org.netbeans.microedition.svg.SVGSplashScreen;
import org.netbeans.microedition.svg.SVGWaitScreen;
import org.netbeans.microedition.util.SimpleCancellableTask;

/**
 *
 */
public class LocationLogger
        extends MIDlet
        implements CommandListener,
                   LocationListener
{

    private boolean midletPaused = false;
    //<editor-fold defaultstate="collapsed" desc=" Generated Fields ">//GEN-BEGIN:|fields|0|
    private Command exitCommand;
    private Command startLoggingCommand;
    private Command stopLoggingCommand;
    private Command unlockKeypadCommand;
    private Command lockKeypadCommand;
    private Command saveCommand;
    private Form missingAPIs;
    private StringItem noteForNoAPIs;
    private SVGWaitScreen acquireFixScreen;
    private SVGSplashScreen fixFailedScreen;
    private Form userInfoForm;
    private StringItem helpString;
    private TextField trainerName;
    private TextField activityDescription;
    private SVGForm mainScreen;
    private SVGSplashScreen transitToMainScreen;
    private SimpleCancellableTask taskWaitForFix;
    private SVGImage svgImageWaitingForFix;
    private SVGImage svgImageFixFailed;
    private SVGImage svgImageMainScreen;
    private SVGImage svgImageTransitToMain;
    //</editor-fold>//GEN-END:|fields|0|
    // </editor-fold>
    /**
     * Millisecond timestamp of the previous location update.
     */
    private static long previousTimestamp = 0;
    /**
     * Calculating-string.
     */
    private final String CALCULATING = "calculating";
    /**
     * String for unauthorized location API.
     */
    private final String LOCATION_API_DENIED = "location API denied";
    /**
     * Latitude in SVG.
     */
    private final String LATITUDE = "latitude";
    /**
     * Longitude in SVG.
     */
    private final String LONGITUDE = "longitude";
    /**
     * Speed in SVG.
     */
    private final String SPEED = "speed";
    /**
     * Timestamp in SVG.
     */
    private final String TIME = "time";
    /**
     * Status in SVG.
     */
    private final String STATUS = "status";
    /**
     * Logging state in SVG.
     */
    private final String LOGGING = "logging";
    /**
     * Keypad state in SVG.
     */
    private final String KEYPAD = "keypad";
    /**
     * Distance in SVG.
     */
    private final String DISTANCE = "distance";
    /**
     * Timestamp for midlet start.
     */
    private long midletStartTimestamp = 0;
    /**
     * Duration for fix acquisition.
     */
    private long gpsFixAcquisitionTimestamp = 0;
    /**
     * Cumulative distance tracked by this midlet.
     */
    private float distance = 0;
    /**
     * Previous location update's coordinates.
     */
    private Coordinates oldCoordinates = new Coordinates( 0, 0, 0 );
    /**
     * Location provider that is used for acquiring location data.
     */
    private LocationProvider locationProvider = null;
    /**
     * String for list of missing APIs.
     */
    private StringBuffer missingAPIList = new StringBuffer( "" );
    /**
     * Flag for indicating keylock being active.
     */
    private boolean keysLocked = false;
    /**
     * Logging state.
     */
    private boolean logging = false;
    /**
     * System property string for JSR 226.
     */
    static final String PROP_VERSION_JSR_226 = "microedition.m2g.version";
    /**
     * System property string for JSR 75.
     */
    static final String PROP_VERSION_JSR_75 = "microedition.io.file.FileConnection.version";
    /**
     * System property string for JSR 179.
     */
    static final String PROP_VERSION_JSR_179 = "microedition.location.version";
    /**
     * System property string for memorycard's root directory in JSR 75.
     */
    static final String PROP_MEMORYCARD_ROOT = "fileconn.dir.memorycard";
    /**
     * File writer thread.
     */
    private GpxWriter gpxWriter = new GpxWriter( this );

    //<editor-fold defaultstate="collapsed" desc=" Generated Methods ">//GEN-BEGIN:|methods|0|
    //</editor-fold>//GEN-END:|methods|0|
    //<editor-fold defaultstate="collapsed" desc=" Generated Method: initialize ">//GEN-BEGIN:|0-initialize|0|0-preInitialize
    /**
     * Initilizes the application.
     * It is called only once when the MIDlet is started. The method is called before the <code>startMIDlet</code> method.
     */
    private void initialize()
    {//GEN-END:|0-initialize|0|0-preInitialize
        gpxWriter.start();
//GEN-LINE:|0-initialize|1|0-postInitialize
    }//GEN-BEGIN:|0-initialize|2|
    //</editor-fold>//GEN-END:|0-initialize|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: startMIDlet ">//GEN-BEGIN:|3-startMIDlet|0|3-preAction
    /**
     * Performs an action assigned to the Mobile Device - MIDlet Started point.
     */
    public void startMIDlet()
    {//GEN-END:|3-startMIDlet|0|3-preAction
        // write pre-action user code here
        apiChecker();//GEN-LINE:|3-startMIDlet|1|3-postAction
        // write post-action user code here
    }//GEN-BEGIN:|3-startMIDlet|2|
    //</editor-fold>//GEN-END:|3-startMIDlet|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: resumeMIDlet ">//GEN-BEGIN:|4-resumeMIDlet|0|4-preAction
    /**
     * Performs an action assigned to the Mobile Device - MIDlet Resumed point.
     */
    public void resumeMIDlet()
    {//GEN-END:|4-resumeMIDlet|0|4-preAction
        // write pre-action user code here
        switchDisplayable(null, getAcquireFixScreen().getSvgCanvas());//GEN-LINE:|4-resumeMIDlet|1|4-postAction
        // write post-action user code here
    }//GEN-BEGIN:|4-resumeMIDlet|2|
    //</editor-fold>//GEN-END:|4-resumeMIDlet|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: switchDisplayable ">//GEN-BEGIN:|5-switchDisplayable|0|5-preSwitch
    /**
     * Switches a current displayable in a display. The <code>display</code> instance is taken from <code>getDisplay</code> method. This method is used by all actions in the design for switching displayable.
     * @param alert the Alert which is temporarily set to the display; if <code>null</code>, then <code>nextDisplayable</code> is set immediately
     * @param nextDisplayable the Displayable to be set
     */
    public void switchDisplayable(Alert alert, Displayable nextDisplayable)
    {//GEN-END:|5-switchDisplayable|0|5-preSwitch
        // write pre-switch user code here
        Display display = getDisplay();//GEN-BEGIN:|5-switchDisplayable|1|5-postSwitch
        if (alert == null)
        {
            display.setCurrent(nextDisplayable);
        }
        else
        {
            display.setCurrent(alert, nextDisplayable);
        }//GEN-END:|5-switchDisplayable|1|5-postSwitch
        // write post-switch user code here
    }//GEN-BEGIN:|5-switchDisplayable|2|
    //</editor-fold>//GEN-END:|5-switchDisplayable|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: commandAction for Displayables ">//GEN-BEGIN:|7-commandAction|0|7-preCommandAction
    /**
     * Called by a system to indicated that a command has been invoked on a particular displayable.
     * @param command the Command that was invoked
     * @param displayable the Displayable where the command was invoked
     */
    public void commandAction(Command command, Displayable displayable)
    {//GEN-END:|7-commandAction|0|7-preCommandAction
        // write pre-action user code here
        if (displayable == acquireFixScreen)//GEN-BEGIN:|7-commandAction|1|53-preAction
        {
            if (command == SVGWaitScreen.FAILURE_COMMAND)
            {//GEN-END:|7-commandAction|1|53-preAction
                // write pre-action user code here
                switchDisplayable(null, getFixFailedScreen().getSvgCanvas());//GEN-LINE:|7-commandAction|2|53-postAction
                // write post-action user code here
            }//GEN-BEGIN:|7-commandAction|3|52-preAction
            else if (command == SVGWaitScreen.SUCCESS_COMMAND)
            {//GEN-END:|7-commandAction|3|52-preAction
                switchDisplayable(null, getTransitToMainScreen().getSvgCanvas());//GEN-LINE:|7-commandAction|4|52-postAction
                // write post-action user code here
            }//GEN-BEGIN:|7-commandAction|5|208-preAction
            else if (command == exitCommand)
            {//GEN-END:|7-commandAction|5|208-preAction
                // write pre-action user code here
                exitMIDlet();//GEN-LINE:|7-commandAction|6|208-postAction
                // write post-action user code here
            }//GEN-BEGIN:|7-commandAction|7|61-preAction
        }
        else if (displayable == fixFailedScreen)
        {
            if (command == SVGSplashScreen.DISMISS_COMMAND)
            {//GEN-END:|7-commandAction|7|61-preAction
                // write pre-action user code here
                exitMIDlet();//GEN-LINE:|7-commandAction|8|61-postAction
                // write post-action user code here
            }//GEN-BEGIN:|7-commandAction|9|209-preAction
        }
        else if (displayable == mainScreen)
        {
            if (command == exitCommand)
            {//GEN-END:|7-commandAction|9|209-preAction
                //! Check for keylock.
                if( keysAreLocked() || loggingIsActive() )
                {
                    return;
                }
                exitMIDlet();//GEN-LINE:|7-commandAction|10|209-postAction
                // write post-action user code here
            }//GEN-BEGIN:|7-commandAction|11|178-preAction
            else if (command == lockKeypadCommand)
            {//GEN-END:|7-commandAction|11|178-preAction
                toggleLock();//GEN-LINE:|7-commandAction|12|178-postAction
            }//GEN-BEGIN:|7-commandAction|13|186-preAction
            else if (command == startLoggingCommand)
            {//GEN-END:|7-commandAction|13|186-preAction
                toggleLogging();//GEN-LINE:|7-commandAction|14|186-postAction
            }//GEN-BEGIN:|7-commandAction|15|189-preAction
            else if (command == stopLoggingCommand)
            {//GEN-END:|7-commandAction|15|189-preAction
                toggleLogging();//GEN-LINE:|7-commandAction|16|189-postAction
            }//GEN-BEGIN:|7-commandAction|17|180-preAction
            else if (command == unlockKeypadCommand)
            {//GEN-END:|7-commandAction|17|180-preAction
                toggleLock();//GEN-LINE:|7-commandAction|18|180-postAction
            }//GEN-BEGIN:|7-commandAction|19|163-preAction
        }
        else if (displayable == missingAPIs)
        {
            if (command == exitCommand)
            {//GEN-END:|7-commandAction|19|163-preAction
                // write pre-action user code here
                exitMIDlet();//GEN-LINE:|7-commandAction|20|163-postAction
                // write post-action user code here
            }//GEN-BEGIN:|7-commandAction|21|121-preAction
        }
        else if (displayable == transitToMainScreen)
        {
            if (command == SVGSplashScreen.DISMISS_COMMAND)
            {//GEN-END:|7-commandAction|21|121-preAction
                // write pre-action user code here
                switchDisplayable(null, getMainScreen().getSvgCanvas());//GEN-LINE:|7-commandAction|22|121-postAction
                // write post-action user code here
            }//GEN-BEGIN:|7-commandAction|23|224-preAction
        }
        else if (displayable == userInfoForm)
        {
            if (command == saveCommand)
            {//GEN-END:|7-commandAction|23|224-preAction
                // write pre-action user code here
                switchDisplayable(null, getMainScreen().getSvgCanvas());//GEN-LINE:|7-commandAction|24|224-postAction
                //! Store user and description.
                String user = "", description = "";
                user = getTrainerName().getString();
                description = getActivityDescription().getString();
                if( user.length() != 0 || description.length() != 0 )
                {
                    //! Store settings.
                    AppSettings.writeSettings( user, description );
                }
                //! Signal quit to file writer.
                gpxWriter.stopLogging( user, description );
            }//GEN-BEGIN:|7-commandAction|25|7-postCommandAction
        }//GEN-END:|7-commandAction|25|7-postCommandAction
        // write post-action user code here
    }//GEN-BEGIN:|7-commandAction|26|
    //</editor-fold>//GEN-END:|7-commandAction|26|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: acquireFixScreen ">//GEN-BEGIN:|49-getter|0|49-preInit
    /**
     * Returns an initiliazed instance of acquireFixScreen component.
     * @return the initialized component instance
     */
    public SVGWaitScreen getAcquireFixScreen()
    {
        if (acquireFixScreen == null)
        {//GEN-END:|49-getter|0|49-preInit
            // write pre-init user code here
            acquireFixScreen = new SVGWaitScreen(getSvgImageWaitingForFix(), getDisplay());//GEN-BEGIN:|49-getter|1|49-postInit
            acquireFixScreen.setTitle("");
            acquireFixScreen.addCommand(getExitCommand());
            acquireFixScreen.setCommandListener(this);
            acquireFixScreen.setFullScreenMode(true);
            acquireFixScreen.setTask(getTaskWaitForFix());//GEN-END:|49-getter|1|49-postInit
            // write post-init user code here
        }//GEN-BEGIN:|49-getter|2|
        return acquireFixScreen;
    }
    //</editor-fold>//GEN-END:|49-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: taskWaitForFix ">//GEN-BEGIN:|54-getter|0|54-preInit
    /**
     * Returns an initiliazed instance of taskWaitForFix component.
     * @return the initialized component instance
     */
    public SimpleCancellableTask getTaskWaitForFix()
    {
        if (taskWaitForFix == null)
        {//GEN-END:|54-getter|0|54-preInit
            // write pre-init user code here
            taskWaitForFix = new SimpleCancellableTask();//GEN-BEGIN:|54-getter|1|54-execute
            taskWaitForFix.setExecutable(new org.netbeans.microedition.util.Executable()
            {
                public void execute() throws Exception
                {//GEN-END:|54-getter|1|54-execute
                    // write task-execution user code here
                    Location waitLocation = null;
                    do
                    {
                        try
                        {
                            waitLocation = locationProvider.getLocation( -1 );
                        }
                        catch( LocationException le )
                        {
                            /**
                             * Timeout expired, ignore it, and eventually
                             * go into sleep.
                             */
                        }
                        if( waitLocation != null )
                        {
                            if( waitLocation.isValid() )
                            {
                                /**
                                 * Now we have a fix, since location is valid.
                                 */
                                oldCoordinates = waitLocation.getQualifiedCoordinates();
                                break;
                            }
                        }
                        Thread.sleep( Utils.ONE_SECOND / 4 );
                    } while( true );
                    gpsFixAcquisitionTimestamp = System.currentTimeMillis();
                    long timeToFirstFix = ( gpsFixAcquisitionTimestamp - midletStartTimestamp ) / 100;
                    updateMainScreenElement( STATUS,
                                             "Fix acquired in " +
                                             String.valueOf( timeToFirstFix / 10.0 ) + " s." );
                    startLocationListener();
                }//GEN-BEGIN:|54-getter|2|54-postInit
            });//GEN-END:|54-getter|2|54-postInit
            // write post-init user code here
        }//GEN-BEGIN:|54-getter|3|
        return taskWaitForFix;
    }
    //</editor-fold>//GEN-END:|54-getter|3|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: svgImageWaitingForFix ">//GEN-BEGIN:|56-getter|0|56-preInit
    /**
     * Returns an initiliazed instance of svgImageWaitingForFix component.
     * @return the initialized component instance
     */
    public SVGImage getSvgImageWaitingForFix()
    {
        if (svgImageWaitingForFix == null)
        {//GEN-END:|56-getter|0|56-preInit
            // write pre-init user code here
            try//GEN-BEGIN:|56-getter|1|56-@java.io.IOException
            {
                svgImageWaitingForFix = (SVGImage) SVGImage.createImage(getClass().getResourceAsStream("/svg/loadScreen.svg"), null);
            }
            catch (java.io.IOException e)
            {//GEN-END:|56-getter|1|56-@java.io.IOException
                e.printStackTrace();
            }//GEN-LINE:|56-getter|2|56-postInit
            // write post-init user code here
        }//GEN-BEGIN:|56-getter|3|
        return svgImageWaitingForFix;
    }
    //</editor-fold>//GEN-END:|56-getter|3|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: fixFailedScreen ">//GEN-BEGIN:|59-getter|0|59-preInit
    /**
     * Returns an initiliazed instance of fixFailedScreen component.
     * @return the initialized component instance
     */
    public SVGSplashScreen getFixFailedScreen()
    {
        if (fixFailedScreen == null)
        {//GEN-END:|59-getter|0|59-preInit
            // write pre-init user code here
            fixFailedScreen = new SVGSplashScreen(getSvgImageFixFailed(), getDisplay());//GEN-BEGIN:|59-getter|1|59-postInit
            fixFailedScreen.setTitle("");
            fixFailedScreen.setCommandListener(this);
            fixFailedScreen.setFullScreenMode(true);//GEN-END:|59-getter|1|59-postInit
            // write post-init user code here
        }//GEN-BEGIN:|59-getter|2|
        return fixFailedScreen;
    }
    //</editor-fold>//GEN-END:|59-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: svgImageFixFailed ">//GEN-BEGIN:|64-getter|0|64-preInit
    /**
     * Returns an initiliazed instance of svgImageFixFailed component.
     * @return the initialized component instance
     */
    public SVGImage getSvgImageFixFailed()
    {
        if (svgImageFixFailed == null)
        {//GEN-END:|64-getter|0|64-preInit
            // write pre-init user code here
            try//GEN-BEGIN:|64-getter|1|64-@java.io.IOException
            {
                svgImageFixFailed = (SVGImage) SVGImage.createImage(getClass().getResourceAsStream("/svg/fixFailed.svg"), null);
            }
            catch (java.io.IOException e)
            {//GEN-END:|64-getter|1|64-@java.io.IOException
                e.printStackTrace();
            }//GEN-LINE:|64-getter|2|64-postInit
            // write post-init user code here
        }//GEN-BEGIN:|64-getter|3|
        return svgImageFixFailed;
    }
    //</editor-fold>//GEN-END:|64-getter|3|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: mainScreen ">//GEN-BEGIN:|116-getter|0|116-preInit
    /**
     * Returns an initiliazed instance of mainScreen component.
     * @return the initialized component instance
     */
    public SVGForm getMainScreen()
    {
        if (mainScreen == null)
        {//GEN-END:|116-getter|0|116-preInit
            // write pre-init user code here
            mainScreen = new SVGForm(getSvgImageMainScreen(), getDisplay());//GEN-BEGIN:|116-getter|1|116-postInit
            mainScreen.setTitle("");
            mainScreen.addCommand(getStartLoggingCommand());
            mainScreen.addCommand(getStopLoggingCommand());
            mainScreen.addCommand(getLockKeypadCommand());
            mainScreen.addCommand(getUnlockKeypadCommand());
            mainScreen.addCommand(getExitCommand());
            mainScreen.setCommandListener(this);
            mainScreen.setFullScreenMode(true);//GEN-END:|116-getter|1|116-postInit
            /**
             * Remove unlock and stop logging commands.
             */
            mainScreen.removeCommand( unlockKeypadCommand );
            mainScreen.removeCommand( stopLoggingCommand );
        }//GEN-BEGIN:|116-getter|2|
        return mainScreen;
    }
    //</editor-fold>//GEN-END:|116-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: svgImageMainScreen ">//GEN-BEGIN:|117-getter|0|117-preInit
    /**
     * Returns an initiliazed instance of svgImageMainScreen component.
     * @return the initialized component instance
     */
    public SVGImage getSvgImageMainScreen()
    {
        if (svgImageMainScreen == null)
        {//GEN-END:|117-getter|0|117-preInit
            // write pre-init user code here
            try//GEN-BEGIN:|117-getter|1|117-@java.io.IOException
            {
                svgImageMainScreen = (SVGImage) SVGImage.createImage(getClass().getResourceAsStream("/svg/mainUI.svg"), null);
            }
            catch (java.io.IOException e)
            {//GEN-END:|117-getter|1|117-@java.io.IOException
                e.printStackTrace();
            }//GEN-LINE:|117-getter|2|117-postInit
            // write post-init user code here
        }//GEN-BEGIN:|117-getter|3|
        return svgImageMainScreen;
    }
    //</editor-fold>//GEN-END:|117-getter|3|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: transitToMainScreen ">//GEN-BEGIN:|120-getter|0|120-preInit
    /**
     * Returns an initiliazed instance of transitToMainScreen component.
     * @return the initialized component instance
     */
    public SVGSplashScreen getTransitToMainScreen()
    {
        if (transitToMainScreen == null)
        {//GEN-END:|120-getter|0|120-preInit
            // write pre-init user code here
            transitToMainScreen = new SVGSplashScreen(getSvgImageTransitToMain(), getDisplay());//GEN-BEGIN:|120-getter|1|120-postInit
            transitToMainScreen.setTitle("");
            transitToMainScreen.setCommandListener(this);
            transitToMainScreen.setFullScreenMode(true);
            transitToMainScreen.setTimeout(1000);//GEN-END:|120-getter|1|120-postInit
            // write post-init user code here
        }//GEN-BEGIN:|120-getter|2|
        return transitToMainScreen;
    }
    //</editor-fold>//GEN-END:|120-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: svgImageTransitToMain ">//GEN-BEGIN:|122-getter|0|122-preInit
    /**
     * Returns an initiliazed instance of svgImageTransitToMain component.
     * @return the initialized component instance
     */
    public SVGImage getSvgImageTransitToMain()
    {
        if (svgImageTransitToMain == null)
        {//GEN-END:|122-getter|0|122-preInit
            // write pre-init user code here
            try//GEN-BEGIN:|122-getter|1|122-@java.io.IOException
            {
                svgImageTransitToMain = (SVGImage) SVGImage.createImage(getClass().getResourceAsStream("/svg/transitToMain.svg"), null);
            }
            catch (java.io.IOException e)
            {//GEN-END:|122-getter|1|122-@java.io.IOException
                e.printStackTrace();
            }//GEN-LINE:|122-getter|2|122-postInit
            // write post-init user code here
        }//GEN-BEGIN:|122-getter|3|
        return svgImageTransitToMain;
    }
    //</editor-fold>//GEN-END:|122-getter|3|

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: apiChecker ">//GEN-BEGIN:|140-if|0|140-preIf
    /**
     * Performs an action assigned to the apiChecker if-point.
     */
    public void apiChecker()
    {//GEN-END:|140-if|0|140-preIf
        // enter pre-if user code here
        if (noMissingAPIs())//GEN-BEGIN:|140-if|1|141-preAction
        {//GEN-END:|140-if|1|141-preAction
            //! APIs have been checked, create member variable objects.
            getLocationProvider();
            getMainScreen();
            switchDisplayable(null, getAcquireFixScreen().getSvgCanvas());//GEN-LINE:|140-if|2|141-postAction
            // write post-action user code here
        }//GEN-BEGIN:|140-if|3|142-preAction
        else
        {//GEN-END:|140-if|3|142-preAction
            // write pre-action user code here
            switchDisplayable(null, getMissingAPIs());//GEN-LINE:|140-if|4|142-postAction
            // write post-action user code here
        }//GEN-LINE:|140-if|5|140-postIf
        // enter post-if user code here
    }//GEN-BEGIN:|140-if|6|
    //</editor-fold>//GEN-END:|140-if|6|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: missingAPIs ">//GEN-BEGIN:|155-getter|0|155-preInit
    /**
     * Returns an initiliazed instance of missingAPIs component.
     * @return the initialized component instance
     */
    public Form getMissingAPIs()
    {
        if (missingAPIs == null)
        {//GEN-END:|155-getter|0|155-preInit
            // write pre-init user code here
            missingAPIs = new Form("Missing APIs", new Item[] { getNoteForNoAPIs() });//GEN-BEGIN:|155-getter|1|155-postInit
            missingAPIs.addCommand(getExitCommand());
            missingAPIs.setCommandListener(this);//GEN-END:|155-getter|1|155-postInit
            // write post-init user code here
        }//GEN-BEGIN:|155-getter|2|
        return missingAPIs;
    }
    //</editor-fold>//GEN-END:|155-getter|2|
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: noteForNoAPIs ">//GEN-BEGIN:|160-getter|0|160-preInit
    /**
     * Returns an initiliazed instance of noteForNoAPIs component.
     * @return the initialized component instance
     */
    public StringItem getNoteForNoAPIs()
    {
        if (noteForNoAPIs == null)
        {//GEN-END:|160-getter|0|160-preInit
            // write pre-init user code here
            String missingJSRs = new String( "Not all of the required " +
                                             "APIs are present on " +
                                             "this device. " +
                                             missingAPIList +
                                             " Cannot continue, " +
                                             "going to exit." );
            noteForNoAPIs = new StringItem("", missingJSRs, Item.PLAIN);//GEN-BEGIN:|160-getter|1|160-postInit
            noteForNoAPIs.setPreferredSize(-1, -1);//GEN-END:|160-getter|1|160-postInit
            // write post-init user code here
        }//GEN-BEGIN:|160-getter|2|
        return noteForNoAPIs;
    }
    //</editor-fold>//GEN-END:|160-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: exitCommand ">//GEN-BEGIN:|162-getter|0|162-preInit
    /**
     * Returns an initiliazed instance of exitCommand component.
     * @return the initialized component instance
     */
    public Command getExitCommand()
    {
        if (exitCommand == null)
        {//GEN-END:|162-getter|0|162-preInit
            // write pre-init user code here
            exitCommand = new Command("Exit", Command.EXIT, 0);//GEN-LINE:|162-getter|1|162-postInit
            // write post-init user code here
        }//GEN-BEGIN:|162-getter|2|
        return exitCommand;
    }
    //</editor-fold>//GEN-END:|162-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: lockKeypadCommand ">//GEN-BEGIN:|177-getter|0|177-preInit
    /**
     * Returns an initiliazed instance of lockKeypadCommand component.
     * @return the initialized component instance
     */
    public Command getLockKeypadCommand()
    {
        if (lockKeypadCommand == null)
        {//GEN-END:|177-getter|0|177-preInit
            // write pre-init user code here
            lockKeypadCommand = new Command("Lock keys", Command.SCREEN, 2);//GEN-LINE:|177-getter|1|177-postInit
            // write post-init user code here
        }//GEN-BEGIN:|177-getter|2|
        return lockKeypadCommand;
    }
    //</editor-fold>//GEN-END:|177-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: unlockKeypadCommand ">//GEN-BEGIN:|179-getter|0|179-preInit
    /**
     * Returns an initiliazed instance of unlockKeypadCommand component.
     * @return the initialized component instance
     */
    public Command getUnlockKeypadCommand()
    {
        if (unlockKeypadCommand == null)
        {//GEN-END:|179-getter|0|179-preInit
            // write pre-init user code here
            unlockKeypadCommand = new Command("Unlock keys", Command.SCREEN, 2);//GEN-LINE:|179-getter|1|179-postInit
            // write post-init user code here
        }//GEN-BEGIN:|179-getter|2|
        return unlockKeypadCommand;
    }
    //</editor-fold>//GEN-END:|179-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: toggleLock ">//GEN-BEGIN:|181-entry|0|182-preAction
    /**
     * Performs an action assigned to the toggleLock entry-point.
     */
    public void toggleLock()
    {//GEN-END:|181-entry|0|182-preAction
//GEN-LINE:|181-entry|1|182-postAction
        //! Toggle the keypad lock -flag.
        keysLocked = ( keysLocked == false );
        if( keysLocked == true )
        {
            getMainScreen().removeCommand( lockKeypadCommand );
            getMainScreen().addCommand( unlockKeypadCommand );
            updateMainScreenElement( KEYPAD, "locked" );
        }
        else
        {
            getMainScreen().removeCommand( unlockKeypadCommand );
            getMainScreen().addCommand( lockKeypadCommand );
            updateMainScreenElement( KEYPAD, "unlocked" );
        }
    }//GEN-BEGIN:|181-entry|2|
    //</editor-fold>//GEN-END:|181-entry|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: startLoggingCommand ">//GEN-BEGIN:|185-getter|0|185-preInit
    /**
     * Returns an initiliazed instance of startLoggingCommand component.
     * @return the initialized component instance
     */
    public Command getStartLoggingCommand()
    {
        if (startLoggingCommand == null)
        {//GEN-END:|185-getter|0|185-preInit
            // write pre-init user code here
            startLoggingCommand = new Command("Start logging", Command.SCREEN, 1);//GEN-LINE:|185-getter|1|185-postInit
            // write post-init user code here
        }//GEN-BEGIN:|185-getter|2|
        return startLoggingCommand;
    }
    //</editor-fold>//GEN-END:|185-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: stopLoggingCommand ">//GEN-BEGIN:|188-getter|0|188-preInit
    /**
     * Returns an initiliazed instance of stopLoggingCommand component.
     * @return the initialized component instance
     */
    public Command getStopLoggingCommand()
    {
        if (stopLoggingCommand == null)
        {//GEN-END:|188-getter|0|188-preInit
            // write pre-init user code here
            stopLoggingCommand = new Command("Stop logging", Command.SCREEN, 1);//GEN-LINE:|188-getter|1|188-postInit
            // write post-init user code here
        }//GEN-BEGIN:|188-getter|2|
        return stopLoggingCommand;
    }
    //</editor-fold>//GEN-END:|188-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: toggleLogging ">//GEN-BEGIN:|190-entry|0|191-preAction
    /**
     * Performs an action assigned to the toggleLogging entry-point.
     */
    public void toggleLogging()
    {//GEN-END:|190-entry|0|191-preAction
//GEN-LINE:|190-entry|1|191-postAction
        //! Do nothing if keys are locked.
        if( keysAreLocked() == true )
        {
            return;
        }
        //! Toggle logging.
        logging = ( logging == false );
        //! Update menus to reflect the state of logging.
        if( logging == true )
        {
            //! Start the file writer thread.
            gpxWriter.startLogging();
            //! Reset the traveled distance.
            distance = 0.0f;
            //! Remove 'start' from menu.
            getMainScreen().removeCommand( startLoggingCommand );
            //! Add 'stop' to menu.
            getMainScreen().addCommand( stopLoggingCommand );
            //! Lock the keys automatically.
            toggleLock();
            updateMainScreenElement( LOGGING, "logging" );
        }
        else
        {
            //! Remove 'stop' from menu.
            getMainScreen().removeCommand( stopLoggingCommand );
            //! Add 'start' to menu.
            getMainScreen().addCommand( startLoggingCommand );
            updateMainScreenElement( LOGGING, "not logging" );
            //! Query for user information.
            promptForUserInfo();
            /**
             * NOTE: gpxWriter thread is instructed to quit writing after
             * user info has been received. That does not happen here.
             */
        }
    }//GEN-BEGIN:|190-entry|2|
    //</editor-fold>//GEN-END:|190-entry|2|

    /**
     * Set up a user info query form.
     */
    private void promptForUserInfo()
    {
        String user = "", description = "";
        AppSettings.getSettings( user, description );
        TextField textField = getTrainerName();
        textField.setString( user );
        textField = getActivityDescription();
        textField.setString( description );
        switchDisplayable( null, getUserInfoForm() );
    }

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: userInfoForm ">//GEN-BEGIN:|212-getter|0|212-preInit
    /**
     * Returns an initiliazed instance of userInfoForm component.
     * @return the initialized component instance
     */
    public Form getUserInfoForm()
    {
        if (userInfoForm == null)
        {//GEN-END:|212-getter|0|212-preInit
            // write pre-init user code here
            userInfoForm = new Form("<null>", new Item[] { getHelpString(), getTrainerName(), getActivityDescription() });//GEN-BEGIN:|212-getter|1|212-postInit
            userInfoForm.addCommand(getSaveCommand());
            userInfoForm.setCommandListener(this);//GEN-END:|212-getter|1|212-postInit
            // write post-init user code here
        }//GEN-BEGIN:|212-getter|2|
        return userInfoForm;
    }
    //</editor-fold>//GEN-END:|212-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: trainerName ">//GEN-BEGIN:|214-getter|0|214-preInit
    /**
     * Returns an initiliazed instance of trainerName component.
     * @return the initialized component instance
     */
    public TextField getTrainerName()
    {
        if (trainerName == null)
        {//GEN-END:|214-getter|0|214-preInit
            // write pre-init user code here
            trainerName = new TextField("Name", null, 32, TextField.ANY);//GEN-LINE:|214-getter|1|214-postInit
            // write post-init user code here
        }//GEN-BEGIN:|214-getter|2|
        return trainerName;
    }
    //</editor-fold>//GEN-END:|214-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: activityDescription ">//GEN-BEGIN:|215-getter|0|215-preInit
    /**
     * Returns an initiliazed instance of activityDescription component.
     * @return the initialized component instance
     */
    public TextField getActivityDescription()
    {
        if (activityDescription == null)
        {//GEN-END:|215-getter|0|215-preInit
            // write pre-init user code here
            activityDescription = new TextField("Description", null, 256, TextField.ANY);//GEN-LINE:|215-getter|1|215-postInit
            // write post-init user code here
        }//GEN-BEGIN:|215-getter|2|
        return activityDescription;
    }
    //</editor-fold>//GEN-END:|215-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: helpString ">//GEN-BEGIN:|222-getter|0|222-preInit
    /**
     * Returns an initiliazed instance of helpString component.
     * @return the initialized component instance
     */
    public StringItem getHelpString()
    {
        if (helpString == null)
        {//GEN-END:|222-getter|0|222-preInit
            // write pre-init user code here
            helpString = new StringItem("", "Enter your name and logging session description.");//GEN-LINE:|222-getter|1|222-postInit
            // write post-init user code here
        }//GEN-BEGIN:|222-getter|2|
        return helpString;
    }
    //</editor-fold>//GEN-END:|222-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: saveCommand ">//GEN-BEGIN:|223-getter|0|223-preInit
    /**
     * Returns an initiliazed instance of saveCommand component.
     * @return the initialized component instance
     */
    public Command getSaveCommand()
    {
        if (saveCommand == null)
        {//GEN-END:|223-getter|0|223-preInit
            // write pre-init user code here
            saveCommand = new Command("Ok", Command.SCREEN, 0);//GEN-LINE:|223-getter|1|223-postInit
            // write post-init user code here
        }//GEN-BEGIN:|223-getter|2|
        return saveCommand;
    }
    //</editor-fold>//GEN-END:|223-getter|2|

    /**
     * Returns a display instance.
     * @return the display instance.
     */
    public Display getDisplay()
    {
        return Display.getDisplay( this );
    }

    /**
     * Exits MIDlet.
     */
    public void exitMIDlet()
    {
        switchDisplayable( null, null );
        destroyApp( true );
        notifyDestroyed();
    }

    /**
     * Called when MIDlet is started.
     * Checks whether the MIDlet have been already started and initialize/starts or resumes the MIDlet.
     */
    public void startApp()
    {
        if( midletPaused )
        {
            resumeMIDlet();
        }
        else
        {
            initialize();
            startMIDlet();
        }
        midletPaused = false;
    }

    /**
     * Called when MIDlet is paused.
     */
    public void pauseApp()
    {
        midletPaused = true;
    }

    /**
     * Called to signal the MIDlet to terminate.
     * @param unconditional if true, then the MIDlet has to be unconditionally terminated and all resources has to be released.
     */
    public void destroyApp( boolean unconditional )
    {
    }

    /**
     * Updater for main screen elements.
     * @param newData New data for the screen.
     * @param key Key into the screen data hashtable.
     */
    public void updateMainScreenElement( String key, String newData )
    {
        getMainScreen().setTraitSafely( key, "#text", newData );
    }

    /**
     * Append an API to the list of the required APIs missing.
     * @param apiName Name of the API to append.
     */
    public void appendMissingAPI( String apiName )
    {
        if( missingAPIList.length() != 0 )
        {
            missingAPIList.append( ", " );
        }
        missingAPIList.append( apiName );
    }

    /**
     * @return True if all required JSRs are in place, false otherwise.
     */
    public boolean noMissingAPIs()
    {
        //! Check for JSR 226: SVG.
        if( System.getProperty( PROP_VERSION_JSR_226 ) == null )
        {
            appendMissingAPI( "JSR 226" );
        }

        //! Check for JSR 75: File Connection.
        if( System.getProperty( PROP_VERSION_JSR_75 ) == null )
        {
            appendMissingAPI( "JSR 75" );
        }
        //! Second check for JSR 75: memorycard directory.
        if( System.getProperty( PROP_MEMORYCARD_ROOT ) == null )
        {
            appendMissingAPI( "memorycard root" );
        }

        //! Check for JSR 179: Location API.
        if( System.getProperty( PROP_VERSION_JSR_179 ) == null )
        {
            appendMissingAPI( "JSR 179" );
        }

        //! Tweak the string.
        if( missingAPIList.length() != 0 )
        {
            if( missingAPIList.toString().indexOf( "," ) != -1 )
            {
                /**
                 * There was a comma in the string, that
                 * implies a plural form.
                 */
                missingAPIList.insert( 0, "Missing APIs are: " );
            }
            else
            {
                missingAPIList.insert( 0, "Missing API is: " );
            }
            missingAPIList.append( "." );
        }

        //! Return.
        return ( missingAPIList.length() == 0 );
    }

    /**
     * Show an alert.
     * @param alertMessage Message to show to the user.
     * @param timeOut Timeout for the alert.
     */
    public void showAlert( String alertMessage, int timeOut )
    {
        Alert dlg = new Alert( null, alertMessage, null, AlertType.INFO );
        dlg.setTimeout( timeOut );
        switchDisplayable( null, dlg );
    }

    /**
     * Checks for the keylock and displays an alert in case of lock.
     * @return Keylock status.
     */
    public boolean keysAreLocked()
    {
        if( keysLocked == true )
        {
            showAlert( "You need to unlock keys first.", Utils.ONE_SECOND );
        }
        return keysLocked;
    }

    /**
     * Checks for the logging status and displays an alert
     * in case it is active.
     * @return Logging status.
     */
    public boolean loggingIsActive()
    {
        if( logging == true )
        {
            showAlert( "You need to stop logging first.", Utils.ONE_SECOND );
        }
        return logging;
    }

    /**
     * State changed.
     * @param locProvider
     * @param newstate
     */
    public void providerStateChanged( LocationProvider locProvider, int newstate )
    {
        String newState = null;
        if( newstate == LocationProvider.AVAILABLE )
        {
            newState = "AVAILABLE";
        }
        else if( newstate == LocationProvider.TEMPORARILY_UNAVAILABLE )
        {
            newState = "TEMPORARILY_UNAVAILABLE";
        }
        else if( newstate == LocationProvider.OUT_OF_SERVICE )
        {
            newState = "OUT_OF_SERVICE";
        }
        updateMainScreenElement( STATUS, newState );
    }

    /**
     * Get location provider.
     */
    private LocationProvider getLocationProvider()
    {
        if( locationProvider == null )
        {
            Criteria cr = new Criteria();
            cr.setCostAllowed( true );

            try
            {
                locationProvider = LocationProvider.getInstance( cr );
                midletStartTimestamp = System.currentTimeMillis();
            }
            catch( LocationException le )
            {
                le.printStackTrace();
            }
        }
        return locationProvider;
    }

    /**
     * Process location update.
     * @param locProvider Location provider used.
     * @param loc The current location.
     */
    public void locationUpdated( final LocationProvider locProvider, final Location loc )
    {
        /**
         * Introduce all variables here to be able to access them
         * within all try-catch -statements.
         */
        long qualifiedTimeStamp = 0;
        QualifiedCoordinates qualifiedCoordinates = null;
        float dashDistance = 0.0f;
        float kmDistance = 0.0f;

        try
        {
            //! Get a qualified timestamp.
            qualifiedTimeStamp = loc.getTimestamp();
            if( qualifiedTimeStamp == 0 )
            {
                //! If GPS doesn't provide a timestamp, use system time.
                qualifiedTimeStamp = System.currentTimeMillis();
            }
        }
        catch( Exception e )
        {
            updateMainScreenElement( TIME, "Catch #1" );
        }

        try
        {
            //! Get qualified coordinates to work on.
            qualifiedCoordinates = loc.getQualifiedCoordinates();
        }
        catch( Exception e )
        {
            updateMainScreenElement( TIME, "Catch #2" );
        }
        if( qualifiedCoordinates != null )
        {
            /**
             * Calculate distance stuff: cumulative distance,
             * and distance between previous and current location.
             */
            try
            {
                dashDistance = distance;
                distance += qualifiedCoordinates.distance( oldCoordinates );
                dashDistance = distance - dashDistance;
                kmDistance = (( int ) distance / 100)/10.0f;
                if( distance > 1000.0 )
                {
                    int intDistance = ( int ) distance / 100;
                    updateMainScreenElement( DISTANCE,
                                             String.valueOf( intDistance / 10.0 ) + " km" );
                }
                else
                {
                    updateMainScreenElement( DISTANCE,
                                             String.valueOf( ( int ) distance ) + " m" );
                }
            }
            catch( Exception e )
            {
                updateMainScreenElement( TIME, "Catch #3" );
            }

            //! Get a calendar instance for ISO-dateformat.
            String locUpdateTimestamp = null;
            try
            {
                locUpdateTimestamp = Utils.calendarToISO8601( null );
                updateMainScreenElement( TIME, locUpdateTimestamp );
            }
            catch( Exception e )
            {
                updateMainScreenElement( TIME, "Catch #4" );
            }

            //! Update location.
            String locUpdateLatitude = null;
            String locUpdateLongitude = null;
            String locUpdateAltitude = null;
            try
            {
                locUpdateLatitude = String.valueOf( qualifiedCoordinates.getLatitude() );
                if( locUpdateLatitude.length() > 9 )
                {
                    locUpdateLatitude = locUpdateLatitude.substring( 0, 9 );
                }
                locUpdateLongitude = String.valueOf( qualifiedCoordinates.getLongitude() );
                if( locUpdateLongitude.length() > 9 )
                {
                    locUpdateLongitude = locUpdateLongitude.substring( 0, 9 );
                }
                locUpdateAltitude = String.valueOf( qualifiedCoordinates.getAltitude() );
                updateMainScreenElement( LATITUDE, locUpdateLatitude );
                updateMainScreenElement( LONGITUDE, locUpdateLongitude );
            }
            catch( Exception e )
            {
                updateMainScreenElement( TIME, "Catch #5" );
            }

            //! Update status.
            try
            {
                if( System.currentTimeMillis() - gpsFixAcquisitionTimestamp > 10000 )
                {
                    //! Remove fix acquisition info after 10 seconds.
                    updateMainScreenElement( STATUS,
                                             Utils.getStateString( locationProvider.getState() ) );
                }
            }
            catch( Exception e )
            {
                updateMainScreenElement( TIME, "Catch #6" );
            }

            //! Calculate speed.
            int speed = 0;
            try
            {
                if( previousTimestamp != 0 )
                {
                    //! Calculate time difference between current and previous call.
                    long timeDifference = qualifiedTimeStamp - previousTimestamp;
                    if( timeDifference != 0 )
                    {
                        //! Calculate speed in km/h.
                        speed = ( int ) Utils.calculateSpeedInKph( dashDistance, timeDifference );
                        updateMainScreenElement( SPEED, String.valueOf( speed ) + " km/h" );
                    }
                    else
                    {
                        //! Hide div/zero into a status message.
                        updateMainScreenElement( SPEED, CALCULATING );
                    }
                }
                else
                {
                    updateMainScreenElement( SPEED, CALCULATING );
                }
            }
            catch( Exception e )
            {
                updateMainScreenElement( TIME, "Catch #7" );
            }

            //! Send the update data to the file writer.
            try
            {
                if( logging == true )
                {
                    gpxWriter.dataUpdate( locUpdateTimestamp,
                                          locUpdateLatitude,
                                          locUpdateLongitude,
                                          locUpdateAltitude,
                                          String.valueOf( speed ),
                                          String.valueOf( kmDistance ) );
                }
            }
            catch( Exception e )
            {
                updateMainScreenElement( TIME, "Catch #8" );
            }
        }

        //! Store coordinates for the next round.
        oldCoordinates = qualifiedCoordinates;

        //! Store timestamp.
        previousTimestamp = qualifiedTimeStamp;
    }

    /**
     * Set up location listener.
     */
    private void setListener()
    {
        if( locationProvider != null )
        {
            locationProvider.setLocationListener( this, 2, -1, -1 );
        }
    }

    /**
     * Start listening to location updates.
     */
    private void startLocationListener()
    {
        new Thread( new Runnable()
        {

            public void run()
            {
                setListener();
            }
        } ).start();
    }
}
